# _*_ coding : utf-8 _*_
# @Time : 2023/10/9 10:46
# @Author : momo
# @File : data_helper
# @Project : bert-textcnn

import pandas as pd
import numpy as np
import config
from tqdm import tqdm
import re
import pickle
import math
from sklearn.model_selection import train_test_split

# 列位置枚举
# 经营表
jy_scope = 1
label1 = 5
label2 = 6
label3 = 7
total_label = 10
new_label_number = 11
# 节点表
n_name = 1
n_level = 3
n_parent_name = 5
n_total_name = 6
# 专利拼接表
tio = 3


class DataHelper:
    total_labels_column = "标签总数"
    label_columns = ["标签1级", "标签2级", "标签3级"]
    sorted_label_columns = ["排序标签1级", "排序标签2级", "排序标签3级"]
    # zl_columns=["tio", "abo"]
    zl_columns = ["tio", "pt"]

    # fn1:'./demo.xlsx'
    # fn2:'企业对应专利 2023-4-12.xlsx'
    # fn3:'半导体产业链v1.xlsx'
    def read_and_merge_content_excl(self, fn1, fn2, fn3):
        _demo = pd.read_excel(fn1)
        _zl = pd.read_excel(fn2)

        # 统计一个公司的标签总数量  "标签数量"
        _demo[self.total_labels_column] = 0
        for c in self.label_columns:
            _demo[self.total_labels_column] += _demo[c].apply(
                lambda x:
                len(str(x).split(";"))
                if x != "" and x is not np.nan
                else 0
            )

        # 计算标签组合的数量
        for i in range(len(self.sorted_label_columns)):
            _demo[self.sorted_label_columns[i]] = _demo[f"标签{i + 1}级"].astype(str).apply(
                lambda x: (sorted(x.split(';'))))
        # -1当分隔符 predict的时候划分
        _demo['总标签'] = _demo.apply(
            lambda row: ";".join(row['排序标签1级'] + ["-1"] + row['排序标签2级'] + ["-1"] + row['排序标签3级']),
            axis=1)
        # 只计算训练集的总标签
        _temp = _demo[-1000:]
        # 取出数量
        total_labels_count_list = _temp["总标签"].value_counts()
        # 转换为dict
        total_labels_count_dict = total_labels_count_list.to_dict()
        # 取出键值
        total_label_list = list(total_labels_count_dict.keys())
        label_new_number_dict = {
            total_label_list[i]: i
            for i in range(len(total_label_list))
        }
        # 新标签数量
        _demo["新标签编号"] = -1
        # print(_demo.columns)
        for i in range(4000, _demo.shape[0]):
            _demo.iloc[i, new_label_number] = label_new_number_dict[_demo.iloc[i, total_label]]

        # 专利信息
        _zl = _zl[["unique_code"] + self.zl_columns]
        # TODO 需要groupby
        # _zl = _zl.groupby('unique_code').agg(','.join).reset_index()

        # concat
        xy = pd.concat([_demo, _zl],axis=1)
        xy = xy.drop(columns=['unique_code'])

        # _zl_left_columns=[i for i in _zl.columns if i not in self.zl_columns]
        # 将"tio"和"abo"移动到前面
        all_columns = ['唯一id', "经营范围", "专利数"] + self.zl_columns + self.label_columns + [
            self.total_labels_column] + self.sorted_label_columns + ["新标签编号"]
        xy = xy.reindex(columns=all_columns)

        # 节点表
        node = self.read_node_and_make_all_name(fn3)
        # 把节点英文匹配中文  使用时value 要去掉" "  因为前面生成有误
        match_dict = {}
        selected_columns_df = node[['节点编码', '总节点名称']]
        match_dict = selected_columns_df.set_index('节点编码').to_dict()['总节点名称']

        # 替换为match_dict的值
        def process_item(i) -> str:
            i = str(i)
            if i.__contains__(";"):
                arr = str(i).split(";")
                arr = [match_dict[a].replace(" ", "") for a in arr]
                return ";".join(arr)
            elif i != "nan" and i is not np.nan:
                return match_dict[i].replace(" ", "")

        for c in self.label_columns:
            xy[c] = xy[c].apply(process_item)

        return xy

    def copy_resample(self,df):
        board = 3
        # 根据标签总数小于3的条件筛选需要复制采样的行
        under_sampled_rows = df[df[self.total_labels_column] < board]
        # 复制需要采样的行
        copied_samples = under_sampled_rows.copy()
        # 可以选择复制多次以增加样本数量，这里我们复制一次
        num_copies = 1
        copied_data = pd.concat([copied_samples] * num_copies, ignore_index=True)
        # 将复制后的数据与原始数据合并，以获取完整的复制采样数据集
        xy = pd.concat([df, copied_data], ignore_index=True)
        # 打乱
        return xy.sample(frac=1, random_state=42)

    # df : xy
    def split_df(self, df):
        # TODO 为了构建一个好的验证集 应该在每一层里面的多少标签分布=9：1
        print("===========resample...============")
        # """
        # 分组方法：
        # 小于1的直接复制1份 train and vali 各一份
        # <=10 vali要一份
        # 剩下的要9：1
        # """
        # grouped = df.groupby('新标签编号')
        # train_dfs, val_dfs = [], []
        #
        # for key, group in grouped:
        #     if len(group) < 2:
        #         # 对于只有1个样本的编号，分一杯羹给验证集
        #         train_dfs.append(group)
        #         # val_dfs.append(group)
        #     elif len(group) < 10:
        #         # 1<len<10 随机选一个给验证集
        #         random_sample = group.sample(n=1, random_state=42)
        #         val_dfs.append(random_sample)  # 随机选择的样本放入验证集
        #         train_group = group.drop(random_sample.index)
        #         train_dfs.append(train_group)  # 剩下的样本放入训练集
        #     else:
        #         # 对于多于10个但少于20个样本的编号，按照9:1的比例划分
        #         train_group, val_group = train_test_split(group, test_size=0.1, random_state=42)
        #         train_dfs.append(train_group)
        #         val_dfs.append(val_group)
        #
        # # 打乱
        # train_df = pd.concat(train_dfs).sample(frac=1, random_state=44)
        # val_df = pd.concat(val_dfs).sample(frac=1, random_state=45)
        #
        # print("=======训练集、验证集分割情况====")
        # print("train_df shape:", train_df.shape)
        # print("val_df shape:", val_df.shape)
        # return train_df, val_df
        board = 3
        # xy=df

        # 根据标签总数小于3的条件筛选需要复制采样的行
        under_sampled_rows = df[df[self.total_labels_column] >=3]
        # 复制需要采样的行
        copied_samples = under_sampled_rows.copy()
        # 可以选择复制多次以增加样本数量，这里我们复制一次
        num_copies = 1
        copied_data = pd.concat([copied_samples] * num_copies, ignore_index=True)
        # 将复制后的数据与原始数据合并，以获取完整的复制采样数据集
        xy = pd.concat([df, copied_data], ignore_index=True)
        # 根据标签总数 分为两组 各取90%、10%组成训练集和验证集（效果优于直接取一九）
        xy_higher = xy[xy[self.total_labels_column] >= board]
        xy_lower = xy[xy[self.total_labels_column] < board]
        # 打乱
        xy_higher=xy_higher.sample(frac=1, random_state=42)
        xy_lower=xy_lower.sample(frac=1, random_state=42)
        # 9：1
        train_count_h = int(xy_higher.shape[0] * 0.9)
        train_count_l = int(xy_lower.shape[0] * 0.9)
        train_xy = pd.concat([xy_higher[:train_count_h], xy_lower[:train_count_l]], ignore_index=True)
        val_xy = pd.concat([xy_higher[train_count_h:], xy_lower[train_count_l:]], ignore_index=True)
        return train_xy,val_xy

    # fn: '半导体产业链v1.xlsx'
    def read_node_and_make_all_name(self, fn):
        node = pd.read_excel(fn, sheet_name="节点编码")
        node["总节点名称"] = ""
        # 层级按0 1 2 3 排下去
        node.sort_values(by="节点层级", inplace=True, ascending=True)

        # 处理3级节点
        def process_level3(p, i):
            prefix = node[node["节点名称"] == p]["总节点名称"]
            i = prefix + "-" + i
            i = str(i)
            # 把多余的部分删除
            return i[i.find(" "):i.find("\n")].strip()

        # 6列是总节点名称
        # 5列是父节点名称
        # 1列是节点名称
        for i in tqdm(range(node.shape[0])):
            # 1级节点是自己
            if node.iloc[i, n_level] == 1:
                node.iloc[i, n_total_name] = node.iloc[i, n_name].strip()
            # 2级节点直接加父节点
            if node.iloc[i, n_level] == 2:
                node.iloc[i, n_total_name] = node.iloc[i, n_parent_name].strip() + "-" + node.iloc[i, n_name].strip()
            # 如果是3级节点的话 就需要生成长的总节点
            elif node.iloc[i, n_level] == 3:
                node.iloc[i, n_total_name] = process_level3(node.iloc[i, n_parent_name], node.iloc[i, n_name])
        return node

    # mlb to fit
    def get_all_node_total_name(self):
        node = self.read_node_and_make_all_name(config.ori_node_path)
        list = []
        for i in range(3):
            l = node[node["节点层级"] == (i + 1)]["总节点名称"]
            list.append(l.tolist())
        return list

    """
    这版load采用每个标签层级一个链表的形式，也就是分开，不拼接不同的层级。
    """

    def load_data(self, df,mode='train'):
        text_list = []
        l1, l2, l3 = [], [], []
        labels_list = []

        if mode == 'train':
            add_times = 2
        else:
            add_times = 1

        for i in tqdm(range(df.shape[0])):
            if str(df.iloc[i, tio])!="nan":
                # s = str(df.iloc[i, jy_scope]) + " 专利：" + re.sub("一种","",str(df.iloc[i, tio]))
                s = re.sub("一种", "", str(df.iloc[i, tio]))
                s = self.wash_text(s)
            else:
                s=str(df.iloc[i, jy_scope])
                s = self.wash_text(s)
            print(i, s)

            for j in range(add_times):
                text_list.append(s)
                # labels 三层各自维护一个[]
                if df.iloc[i, label1] is not None:
                    l1.append(str(df.iloc[i, label1]).split(";"))
                else:
                    l1.append([])
                if df.iloc[i, label2] is not None:
                    l2.append(str(df.iloc[i, label2]).split(";"))
                else:
                    l2.append([])
                if df.iloc[i, label3] is not None:
                    l3.append(str(df.iloc[i, label3]).split(";"))
                else:
                    l3.append([])
        labels_list.append(l1)
        labels_list.append(l2)
        labels_list.append(l3)

        return text_list, labels_list

    # # df : xy, columns=['唯一id', '经营范围', '专利数','tio', 'abo', '标签1级', '标签2级', '标签3级', '标签总数']
    # def load_data(self, df):
    #     text_list = []
    #     labels_list = []
    #     for i in tqdm(range(df.shape[0])):
    #         if df.iloc[i, tio] is not None:
    #             # text_list.append(str(df.iloc[i, 1]) + "还有专利" + str(df.iloc[i, 3]) + ":" + str(df.iloc[i, 4]))
    #             s = str(df.iloc[i, jy_scope]) + "还有专利:" + str(df.iloc[i, tio])
    #             s = self.wash_text(s)
    #             text_list.append(s)
    #         else:
    #             s = str(df.iloc[i, jy_scope])
    #             s = self.wash_text(s)
    #             text_list.append(s)
    #
    #         if df.iloc[i, label3] is not None:
    #             labels_list.append(
    #                 str(df.iloc[i, label1]).split(";") + str(df.iloc[i, label2]).split(";") + str(df.iloc[i, label3]).split(";"))
    #         elif df.iloc[i, label2] is not None:
    #             labels_list.append(
    #                 str(df.iloc[i, label1]).split(";") + str(df.iloc[i, label2]).split(";")
    #             )
    #         elif df.iloc[i, label1] is not None:
    #             labels_list.append(
    #                 str(df.iloc[i, label1]).split(";")
    #             )
    #         else:
    #             # 无label
    #             labels_list.append([])
    #     return text_list, labels_list

    @staticmethod
    def wash_text(text):
        # 没有使用正则原因是 正则无法控制 刷坏了数据
        # 删除赘述
        words_to_remove = ['〓',
                           '技术服务、技术开发、技术咨询、技术交流、技术转让、技术推广',
                           '一般项目：', '一般经营项目是：', '一般经营项目：', '许可项目：', '许可经营项目是：',
                           '依法须经批准的项目，经相关部门批准后依批准的内容开展经营活动','依法须经批准的项目，经相关部门批准后方可开展经营活动',
                           '国家法律法规禁止经营的项目除外；涉及许可经营的产品需取得许可证后方可经营',
                           '国家禁止或限制的货物或技术除外',
                           '除依法须经批准的项目外，凭营业执照依法自主开展经营活动',
                           '具体经营项目以相关部门批准文件或许可证件为准',
                           '市场主体依法自主选择经营项目，开展经营活动',
                           '涉及配额、许可证管理商品的，按国家有关规定办理申请',
                           '不得从事国家和本市产业政策禁止和限制类项目的经营活动',
                           '法律、行政法规、国务院决定禁止的项目除外，限制的项目须取得许可后方可经营',
                           '不含国家禁止或限制进出口的货物或技术',
                           '但国家限定企业经营或禁止进出口的商品和技术除外',
                           '不含特种设备制造',
                           '具体经营项目以审批结果为准',
                           '不含专营、专控、专卖商品',
                           '上述经营范围涉及许可经营项目的，凭许可证明文件或批准证书在有效期内经营，未经许可不得经营',
                           '国家限制和禁止进出口的货物和技术除外',
                           '（以上项目不涉及外商投资准入特别管理措施）',
                           '不涉及国营贸易管理商品',
                           '国家禁止或限制进出口的货物和技术除外',
                           '国家限定公司经营或禁止进出口的商品和技术除外',
                           '（以上制造项目均不含《市场准入负面清单》禁止类项目）',
                           '涉及许可经营的凭许可证经营',
                           '（法律、行政法规禁止的项目除外；法律、行政法规限制的项目须取得许可后方可经营）',
                           '\(法律、行政法规、国务院决定规定在登记前须经批准的项目除外），',
                           '（国家有专项规定的从其规定）',
                           '除国家限定公司经营或禁止进出口的货物及技术',
                           '（以上根据法律、行政法规、国务院决定等规定需要审批的，依法取得相关审批文件后方可经营）',
                           '法律、行政法规、国务院决定禁止的项目除外,限制的项目须取得许可后方可经营',
                           '（以上涉及法律、行政法规、国务院决定禁止的项目除外，限制的项目须取得许可方经营）',
                           '（国家禁止和限制的除外，涉及许可证的制度凭证经营）',
                           '（依法须经审批的项目，经相关部门审批后方可开展经营活动）',
                           '（国家限定企业经营或禁止进出口的商品及技术除外）',
                           '（国家限定企业经营或禁止进出口的商品和技术除外）',
                           '（以上均不涉及外商投资准入特别管理措施项目；涉及资质许可的项目须取得许可后方可经营）',
                           '（不含限制项目）',
                           '以上各项法律、行政法规规定禁止的项目除外',
                           '法律、行政法规规定限制的项目须取得许可证后方可经营',
                           '国家禁止或涉及行政审批的货物和技术进出口除外',
                           '（涉及许可经营项目，应取得相关部门许可后方可经营）',
                           '法律、行政法规禁止经营的项目除外',
                           '法律、行政法规限制经营的项目取得许可证后方可经营',
                           '国家禁止或涉及行政审批的货物及技术进出口除外',
                           '国家禁止和限制的除外，涉及许可证的凭证经营',
                           '以上商品进出口不涉及国营贸易管理商品',
                           '【分支机构经营】',
                           '\*.*\*',
                           '许可经营项目是：无',
                           '许可经营项目：无',
                           '（法律、行政法规.*的项目除外）',
                           '（以上.*项目）',
                           '（以上不含专营，专控，专卖，限制商品）',
                           '（法律、行政法规、国务院决定禁止的项目除外，限制的项目需取得许可后方可经营）',
                           '(涉及工业行业另行选择生产地生产或者经营地经营)',
                           '（须取得许可或批准后方可经营）',
                           '(涉及工业行业另行选择生产地生产或者经营地经营)',
                           '(涉及工业行业另行选择生产地生产或者经营地经营)',
                           '（以上经营范围凡涉及国家有专项专营规定的从其规定）',
                           '（不含法律、行政法规、国务院决定禁止项目和需前置审批的项目）',
                           '（不含法律、行政法规、国务院决定禁止项目和需前置审批的项目）',
                           '（法律、行政法规、国务院决定规定须经批准的经营项目，凭批准文件、证件经营。）',
                           '(法律，行政法规，国务院决定禁止的项目除外，限制的项目须取得许可后方可经营)',
                           '（生产凭深南环批【2011】52317号环保批复经营）',
                           '（不含《市场准入负面清单》及《江门市投资准入禁止限制目录》事项）',
                           '（国家限定企业经营或禁止进出口的商品除外）',
                           '\(法律、行政法规、国务院决定规定在登记前须经批准的项目除外）',
                           ]
        # 弃用正则
        # wp=[
        #      "国家.*除外",
        #      "国家禁止.*除外",
        #     "国家禁止.*经营",
        #     # "（以上.*）",
        #     # "（以上.*\)",
        #     # "\[以上.*\]",
        #     # "\(以上.*\)",
        #     # "（.*除外）",
        #     # "（不含.*）",
        #     '【分支机构经营】',
        #     '〓',
        #     # '\(除.*\)',
        #     # '（除.*）',
        #     # '（依法.*为准）',
        #     # '\(依法.*为准\)',
        #     # '\(依法.*为准）',
        #     # '（凭.*）',
        #     # '（企业.*）',
        #     # '（法律.*）',
        #     # '\(法律.*）',
        #     # '\(法律.*\)',
        #     # '（需.*）',
        #     # '\(不.*）',
        #     # '（上述.*）',
        #     # '（市场.*）',
        #     # '（具体.*）',
        #     # '（不涉及.*）',
        #     # '（领取.*）',
        #     # '（，.*经营）',
        #     '专利信息:nan',
        #     '\*.*\*',
        #     '许可经营项目是：无',
        #     '许可经营项目：无',
        #     # '（涉及.*）',
        #     '法律.*经营。',
        #     '市场主体.*活动。',
        #     '一般项目：', '一般经营项目是：', '一般经营项目：', '许可项目：', '许可经营项目是：'
        # ]

        pattern1 = '|'.join(words_to_remove)  # 构建正则表达式模式
        # 使用正则表达式替换
        text = re.sub(pattern1, "", text)

        char_to_remove=[
            '（）',
            '\(\)',
            '\(，\)',
            '（，）',
            '（。）',
            '\(。\)',
            '（以上）',
            '【】',
            '（，）',
        ]
        p2='|'.join(char_to_remove)
        text=re.sub(p2,'',text)

        # 更改有问题的标点符号
        text=re.sub("。。","。",text)
        text=re.sub("。，","。",text)
        text=re.sub("，。","。",text)
        text=re.sub("。,","。",text)
        text=re.sub("；，","；",text)
        text=re.sub("；。","。",text)
        text=re.sub('\(\)',"",text)
        text=re.sub("，但。",'。',text)

        return text

    @staticmethod
    def make_node_dict(key_name, value_name):
        node = pd.read_excel(config.ori_node_path, sheet_name="节点编码")
        selected_columns_df = node[[key_name, value_name]]
        name_df = selected_columns_df["节点名称"].apply(lambda x: x.replace(" ", ""))
        selected_columns_df.update(name_df)
        # print(selected_columns_df["节点名称"][100:150])
        _dict = selected_columns_df.set_index(key_name).to_dict()[value_name]
        return _dict

    @staticmethod
    def get_label_nums_dict_arr(df):
        label_nums_dict_arr = []
        rematch_dict = DataHelper.make_node_dict("节点名称", "节点编码")
        for c in DataHelper.label_columns:
            d = DataHelper.get_label_nums_dict_by_column(df, c, rematch_dict)
            label_nums_dict_arr.append(d)
        print(label_nums_dict_arr)
        return label_nums_dict_arr

    @staticmethod
    def get_label_nums_dict_by_column(df, column, rematch_dict):
        nums_dict = {}
        # 按列获取标签
        for s in df[column]:
            if s == "" or s is None: continue
            if s not in nums_dict:
                s = str(s).split(';')
                for i in s:
                    # i=rematch_dict[i[i.rfind("-")+1:]]
                    if i not in nums_dict:
                        nums_dict[i] = 0
                        nums_dict[i] += 1
                    else:
                        nums_dict[i] += 1
            else:
                nums_dict[s] += 1
        print(nums_dict)
        return nums_dict

    @staticmethod
    def make_weights_list():
        dh = DataHelper()
        xy = dh.read_and_merge_content_excl(config.ori_demo_path, config.ori_zl_path, config.ori_node_path)
        # 生成标签数量字典以生成标签权重
        dict_arr = DataHelper.get_label_nums_dict_arr(xy)
        label_counts = []
        for i in range(3):
            # 取出indexes对应的labels  ./data/mlb{i}.pkl
            mlb = pickle.load(open(config.mlb_dir + str(i) + ".pkl", 'rb'))
            label_list = mlb.classes_.tolist()
            count_list = []
            curr_dict = dict_arr[i]
            # print("curr dict is",curr_dict)
            for l in label_list:
                # print(l)
                # l=l[l.rfind("-")+1:]
                if l in curr_dict.keys():
                    count_list.append(curr_dict[l])
            # print(len(count_list))
            label_counts.append(count_list)
        # 对于第一个输出层的标签权重
        # label_weights_output1 = [1 / count if count < 8 else 1 / 8 for count in label_counts[0]]
        # 计算每个子列表的倒数值
        # print(label_counts)
        reciprocal_label_counts = [[math.log(math.log(2832 / count)) for count in sublist] for sublist in label_counts]
        # print(reciprocal_label_counts)
        return reciprocal_label_counts

    def over_resample(self, df):
        board = 3
        # 根据标签总数小于3的条件筛选需要复制采样的行
        # under_sampled_rows = df[df[self.total_labels_column] < 3]

        # 选择理由： >=4 的标签更有可能是冷标签  提升冷标签的出现率
        under_sampled_rows = df[df[self.total_labels_column] >= board]
        # 复制需要采样的行
        copied_samples = under_sampled_rows.copy()
        # 可以选择复制多次以增加样本数量，这里我们复制一次
        num_copies = 1
        copied_data = pd.concat([copied_samples] * num_copies, ignore_index=True)
        # 将复制后的数据与原始数据合并，以获取完整的复制采样数据集
        return pd.concat([df, copied_data], ignore_index=True)


def my_print(title, content):
    print("=============={}================".format(title))
    print(content)

def test_zl():
    _zl = pd.read_excel(config.ori_zl_path)

    _zl=_zl[["unique_code",'tio','pt']]
    # _zl = _zl.groupby('unique_code').agg(','.join).reset_index()

def print_1000(df,columns):
    for i in range(20):
        print(f"=====第{i+1}个50条======")
        print(df[columns][-1000+i*50:-1000+(i+1)*50])



if __name__ == "__main__":
    # 测试加载
    dh = DataHelper()
    # dh.get_all_node_total_name()
    # node=dh.read_node_excl(config.ori_node_path)
    # xy = dh.read_and_merge_content_excl(config.ori_demo_path, config.ori_zl_path, config.ori_node_path)
    # arr = dh.get_label_nums_dict_arr(xy)
    # dh.load_data(xy)
    test_zl()
